/*
 * zoj 1610 Count the Colors
 * 题目网址：https://zoj.pintia.cn/problem-sets/91827364500/problems/91827365109
 * 题解: 区间[l,r]表示点[l-1, r]之间的线段的颜色,最后遍历线段树可以算出每个叶子节点的颜色
 * 线段树 说明
 * seg_data_type： 线段树维护的信息的数据类型
 * OP_TYPE: 区间操作类型
 * NODE_DATA: 节点存储数据的类型
 * SegNode::data_init 节点数据初始化
 * SegNode::leaf_init 叶子节点数据初始化
 * SegNode::push_down [l,r]区间维护的数据往下分配给子节点
 * SegNode::push_up 据子节点的信息更新区间[l,r]维护的信息
 * SegNode::query 查询或更新子树
 * SegNode::merge_val 合并从子树查询到的结果
 * SegNode::output 输出节点,调试用
 * SegTree::start_pos 区间起始位置0或者1
 * SegTree::seg_query_init_val 查询区间的初始值,例如区间求和为0，区间求乘积为1
 * SegTree::leaf_default_val 叶子节点默认值(如果传给SegTree::init_tree的data为NULL则需要设置leaf_default_val)
 */

#include <stdio.h>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;


bool color_pair_cmp(pair<int, int> a, pair<int, int> b){
	return a.first < b.first;
}

typedef int seg_data_type; 
const int NO_COLOR = -1;
enum OP_TYPE{
	OP_SET_COLOR, 	//给每个元素设置元素
};

enum NODE_DATA{
	COLOR, 	//标记颜色
	NODE_DATA_NUM
};

class SegTree;
class SegNode{
	friend SegTree;
private:
	seg_data_type 		data[NODE_DATA_NUM];
	SegNode * 			children[2]; 	//子节点
	int l; 				//区间左边界
	int r; 				//区间右边界
public:

	void data_init(){
		data[COLOR] = NO_COLOR;
	}

	void leaf_init(seg_data_type d){
		data[COLOR] = d;
	}

	void push_down(){
		for(int i = 0; i < 2; i++){
			if(children[i]){
				if(data[COLOR] != NO_COLOR){
					children[i]->data[COLOR] = data[COLOR];
				}
			}
		}
		data[COLOR] = NO_COLOR;
	}

	//根据子节点的信息更新信息
	void push_up(){
		if(children[0]->data[COLOR] == children[1]->data[COLOR]){
			data[COLOR] = children[0]->data[COLOR];
		}
	}

	
	seg_data_type query(int op, int k){
		seg_data_type v = 0;
		// 更新节点
		if(op == OP_SET_COLOR){
			data[COLOR] = k;
		}
		return v;
	}
	
	void merge_val(int op, seg_data_type& val, seg_data_type sub_tree_val){
	}

	void output(){
		printf("[%d,%d] data[COLOR]=%d\n", l, r, data[COLOR]);
	}

	SegNode(){
		init();
	}

	void init(){
		l = r = 0;
		children[0] = children[1] = NULL;
		data_init();
	}

	bool is_leaf(){
		return l == r;
	}

	bool not_leaf(){
		return !is_leaf();
	}

	//判断线段的区间是否完全在区间[l, r]里
	bool inside(int l, int r){
		return l <= this->l && this->r <= r;
	}

	//判断线段的区间是否和区间[l, r]完全不相交
	bool no_intersect(int l, int r){
		return this->r < l || r < this->l;
	}

	bool intersect(int l, int r){
		return !no_intersect(l, r);
	}

	int len(){
		return r - l + 1;
	}
	
	//中点
	int mid(){
		return (l + r) >> 1;
	}

};

class SegTree{
private:
	int 			start_pos;  			//区间起始位置，0或者1
	seg_data_type 	seg_query_init_val;  	//查询区间的初始值
	seg_data_type 	leaf_default_val;  		//叶子节点默认值
	vector<SegNode> tree;
	int 			node_num; 	//节点个数
	vector<int> 	colors;
	int 			seg_num;
public:
	SegTree(int init_num){
		start_pos = 1;
		seg_query_init_val = 0;
		leaf_default_val = NO_COLOR;
		tree.resize(init_num);
		colors.resize(init_num + 1);
		colors[0] = NO_COLOR;
	}

	//初始化线段树
	void init_tree(int *data, int n){
		//线段树节点的个数为2*-1
		if(2 * n - 1 > tree.size()){
			tree.resize(2 * n - 1);
		}
		if(n + 1 > colors.size()){
			colors.resize(n + 1);
		}
		node_num = 0;
		seg_num = n;
		init_seg(&tree[node_num++], data, n, start_pos, n - 1 + start_pos);
	}

	//初始化区间
	void init_seg(SegNode *node, int *data, int n, int l, int r){
		node->init();
		node->l = l;
		node->r = r;
		if(l == r){
			if(data != NULL){
				node->leaf_init(data[l - start_pos]);
			}else{
				node->leaf_init(leaf_default_val);
			}
		}else{
			int mid = node->mid();
			for(int i = 0; i < 2; i ++){
				node->children[i] = &tree[node_num++];
			}
			// mid属于左子树
			init_seg(node->children[0], data, n, l, mid);
			init_seg(node->children[1], data, n, mid + 1, r);
			node->push_up();
		}
	}

	SegNode* root(){
		return &tree[0];
	}


	seg_data_type query_tree(int op, int l, int r){
		return query(root(), op, l, r, 0);
	}

	seg_data_type query_tree(int op, int l, int r, int k){
		return query(root(), op, l, r, k);
	}

	seg_data_type query(SegNode *node, int op, int l, int r, int k){
		seg_data_type v = seg_query_init_val;
		if(node->inside(l, r)){
			v = node->query(op, k);
		}else if(node->not_leaf() && node->intersect(l, r)){
			node->push_down();
			//区间相交往下降 
			int mid = node->mid();
			if(l <= mid){
				node->merge_val(op, v, query(node->children[0], op, l, min(mid, r), k));
			}
			if(r > mid){
				node->merge_val(op, v, query(node->children[1], op, max(mid + 1, l), r, k));
			}
			node->push_up();
		}
		return v;
	}

	void output(){
		for(int i = 0; i < node_num; i++){
			tree[i].output();
		}
	}

	void traverse_tree(){
		traverse(root());
	}

	void traverse(SegNode *node){
		if(node->is_leaf()){
			colors[node->l] = node->data[COLOR];
		}else{
			node->push_down();
			traverse(node->children[0]);
			traverse(node->children[1]);
			node->push_up();
		}
	}

	vector<pair<int, int> > get_color_nums(){
		traverse_tree();
		map<int, int> color_map;
		map<int, int>::iterator map_it;
		int color;
		for(int i = 1; i <= seg_num + 1; i++){
			color = NO_COLOR;
			//计算最后一段
			if(i == seg_num + 1){
				color = colors[i-1];
			}else{
				if(i > 1 && colors[i] != colors[i - 1]){
					color = colors[i-1];
				}
			}
			if(color != NO_COLOR){
				map_it = color_map.find(color);
				if(map_it != color_map.end()){
					color_map[color] = (map_it->second) + 1;
				}else{
					color_map[color] = 1;
				}
			}
		}
		vector<pair<int, int> > color_nums;
		for(map_it = color_map.begin(); map_it != color_map.end(); map_it++){
			color_nums.push_back(make_pair(map_it->first, map_it->second));
		}
		sort(color_nums.begin(), color_nums.end(), color_pair_cmp);
		return color_nums;
	}

};

int main(int argc, char* argv[])
{
	const int NUM = 8000;
	//构建线段树
	SegTree seg_tree(2 * NUM);
	int n, l, r, c;
	while(scanf("%d", &n) != EOF){
		//线段树根节点长度固定为NUM
		seg_tree.init_tree(NULL, NUM);
		for(int i = 0; i < n; i++){
			scanf("%d%d%d", &l, &r, &c);
			if(l > r){
				swap(l, r);
			}
			l++;
			if(l <= r){
				seg_tree.query_tree(OP_SET_COLOR, l, r, c);
			}
		}
		vector<pair<int, int> > color_nums = seg_tree.get_color_nums();
		vector<pair<int, int> >::iterator it;
		for(it = color_nums.begin(); it != color_nums.end(); it++){
			printf("%d %d\n", it->first, it->second);
		}
		printf("\n");
	}
    return 0;
}
